62 类的菱形继承问题

451次阅读
没有评论

共计 3746 个字符,预计需要花费 10 分钟才能阅读完成。

一. 类的分类

在前面一章我们简单介绍了一些类的分类

1. 新式类

  • 继承了 object 的类以及该类的子类, 都是新式类 (Python3 中统一都是新式类)
  • Python3 中如果一个类没有继承任何类, 则 默认会继承 object 类, 也就是Python3 中所有的类 都是 新式类
🍔在 "Python3" 中
class Default:  # 默认继承 "object"
    pass

print(Default.__bases__)  # (<class 'object'>,)

2. 经典类

  • 没有继承 object 的类以及该类的子类, 都是经典类 (只有 Python2 中才区分新式类和经典类)
  • Python2 中如果一个类没有继承任何类, 它 不会继承object 类, 所以Python2 中才有经典类
🍔在 "Python2" 中
class Default(object):  # 新式类
    pass

class Animal:  # 经典类
    pass

🍔"Python2" 中 "print" 语法
print Default.__bases__  # (<class 'object'>,)
print Animal.__bases__   # ()

ps : 新式类与经典类的属性查找顺序是不一样的


二. 菱形继承问题

上面说到 Python 支持多继承, 但新式类与经典类的属性查找顺序是不一样的

java 语言中,它不支持多继承,只支持继承一个父类

python 语言,支持多继承,比如 A(B,C,D)

1. 非菱形结构

  • 非菱形结构下, 经典类与新式类的属性 查找顺序是一样的

  • 如果继承关系为非菱形结构,则会按照先找 B 这一条分支,然后再找 C 这一条分支,最后找 D 这一条分支的顺序直到找到我们想要的属性

  • 非菱形结构 (两条查询路径最后分别继承了 F1 和 F4)

62 类的菱形继承问题

class F1:
    def s1(self):
        print('F1:s1')
    def s2(self):
        print('F1:s2')

class F2(F1):
    # def s1(self):
    #     print('F2:s1')
    def s2(self):
        print('F2:s2')

class F4():
    def s1(self):
        print('F4:s1')
    def s2(self):
        print('F4:s2')

class F3(F2,F4):
    def s1(self):
        super().s1()  # 调用父类的 s1 方法, 到底使用了哪个父类的 s1
    def s2(self):
        print('F3:s2')


f1 = F3()
f1.s1()  # F1:s1
  • 查找顺序 : F3 ===> F2 ===> F1

2. 菱形结构

  • 如果继承关系为菱形结构,即子类的父类最后继承了同一个类,那么属性的查找方式有两种:
  • 新式类 -----> 广度优先
  • 经典类 -----> 深度优先

1、新式类 -----> 广度优先

  • 不找多个类最后继承的同一个类,直接去找下一个父类
  • __mro__ : 只有新式类才有的属性, 可查看属性查找的顺序

62 类的菱形继承问题

class G(object):
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class B(E):
    # def test(self):
    #     print('from B')
    pass

class F(G):
    # def test(self):
    #     print('from F')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D(G):
    # def test(self):
    #     print('from D')
    pass

class A(B, C, D):
    def test(self):
        print('from A')

obj = A()
obj.test()
print(A.__mro__)
''' 查找顺序
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <type 'object'>)
'''

2、经典类 -----> 深度优先

  • 从左到右选路, 一条路走到黑

62 类的菱形继承问题

#coding:utf-8
# python2 解释器

class G():
    pass
    def test(self):
        print('G---test')

class F(G):
    pass
    # def test(self):
    #     print('FFF---test')
class E(G):
    pass
    # def test(self):
    #     print('EEE---test')
class D(G):
    pass
    # def test(self):
    #     print('DDD---test')
class B(E):
    pass
    # def test(self):
    #     print('BBB---test')
class C(F):
    pass
    # def test(self):
    #     print('CCC---test')

class A(B,C,D):
    pass
    # def test(self):
    #     print('AAA---test')

a=A()
a.test()  # G---test

三. 小示例

1. 新式类

  • 菱形结构示例 (子类最后继承了同样的类, 都继承了 A1 和 A2), 可以看成两个菱形

62 类的菱形继承问题

class A1:
    def foo(self):
        print('A1_foo')

class A2:
    def foo(self):
        print("A2_foo")

    def bar(self):
        print("A2_bar")

class B1(A1, A2):
    pass

class B2(A1, A2):
    def bar(self):
        print("B2_bar")

class C(B1, B2):
    pass

c = C()
c.foo()  # A1_foo
c.bar()  # B2_bar

print(C.__mro__)
''' 输出
(<class '__main__.C'>, <class '__main__.B1'>, <class '__main__.B2'>, <class '__main__.A1'>, <class '__main__.A2'>, <class 'object'>)
'''
  • c.foo() 的查找顺序 : C ==> B1 ===> B2 ==> A1
  • c.bar() 的查找顺序 : C ==> B1 ==> B2

2. 经典类

62 类的菱形继承问题

#coding:utf-8
# python2.7 解释器
class A1():
    def foo(self):
        print 'A1_foo'

class A2():
    def foo(self):
        print "A2_foo"

    def bar(self):
        print "A2_bar"

class B1(A1, A2):
    pass

class B2(A1, A2):
    def bar(self):
        print "B2_bar"

class C(B1, B2):
    pass

c = C()
c.foo()  # A1_foo
c.bar()  # A2_bar
  • c.foo() 的查找顺序 : C ==> B1 ===> A1
  • c.bar() 的查找顺序 : C ==> B1 ==> A1 ===> A2

四. c3 算法与 mro

1. 简介

为了实现继承,python 会在 MRO 列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止

而这个 MRO 列表的构造是通过一个 C3 线性化算法 来实现的

我们不去深究这个算法的数学原理,它实际上就是合并所有父类的 MRO 列表并遵循如下三条准则 :

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

2.__mro__ 方法使用

其实上面我们已经使用了这种方法, 下面再补充一点

  • 只有 新式类 才可以使用 (Python2中的类如果没继承 object 就无法使用)
  • [类名]__mro__ : 打印属性查找顺序, 是一个元组
  • [类名].mro() : 打印属性查找顺序, 是一个列表
🍔Python2 中
#coding:utf-8

class C(object):  # 继承 object 类, 如果不继承, 调用 mro 方法会报错 
    def run(self):
        print "run_C"

class B(C):
    pass

class A(B):
    pass

a = A()
a.run()  # run_C
print A.__mro__  
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>)

print A.mro()  
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>]

🍔Python3 中
class C:  # 已经自动继承了 object 类
    def run(self):
        print("run_C")

class B(C):
    pass

class A(B):
    pass

a = A()
a.run()  # run_C
print(A.__mro__)
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)

print(A.mro())
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计3746字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)